#include "stdafx.h"



//#include <atlbase.h>
#include <oledbdm.h>

#include "DMMetadataAndFactory.h"
#include "DMAttributeSet.h"
#include "DMAlgorithm.h"
using namespace PluginAlgorithms;
using namespace System::Globalization;


AlgorithmMetadataBase::AlgorithmMetadataBase()
{
    m_rgSupInputContentTypes  =   NULL;
    m_cSupInputContentTypes   =   0;

    m_rgSupPredictContentTypes  =   NULL;
    m_cSupPredictContentTypes   =   0;

    m_rgSupModelingFlags        =   NULL;
    m_cSupModelingFlags         =   0;

    m_rgSupFunctions            =   NULL;
    m_cSupFunctions             =   0;


	CONTEXT_TLS_SLOT	=	::TlsAlloc();
	if( CONTEXT_TLS_SLOT == TLS_OUT_OF_INDEXES )
	{
		throw gcnew System::Exception(gcnew System::String(L"TlsAlloc failure"));
	}
	::TlsSetValue( CONTEXT_TLS_SLOT, (LPVOID)NULL);

	CONTEXTREF_TLS_SLOT	=	::TlsAlloc();
	if( CONTEXTREF_TLS_SLOT == TLS_OUT_OF_INDEXES )
	{
		throw gcnew System::Exception(gcnew System::String(L"TlsAlloc failure"));
	}
	::TlsSetValue( CONTEXTREF_TLS_SLOT, (LPVOID)NULL);


    m_context = gcnew ContextServices();

	BuildCustomFunctionInfo();
}


// Destructor
AlgorithmMetadataBase::~AlgorithmMetadataBase()
{
    
    if( m_rgSupInputContentTypes )
    {
        delete[]    m_rgSupInputContentTypes;
    }

    if( m_rgSupPredictContentTypes )
    {
        delete[]    m_rgSupPredictContentTypes;
    }

    if( m_rgSupModelingFlags )
    {
        delete[]    m_rgSupModelingFlags;
    }

    if( m_rgSupFunctions )
    {
        delete[]    m_rgSupFunctions;
    }

    if( m_params != nullptr )
    {
        m_params = nullptr;
    }

	::TlsFree(CONTEXT_TLS_SLOT);
	::TlsFree(CONTEXTREF_TLS_SLOT);
}



// Interface ICLR_DMALgorithmFactory
HRESULT AlgorithmMetadataBase::ExportedCreateAlgorithm(
                                IDMModelServices* in_pModel,
                                IDMAlgorithm**    out_ppAlgo)
{ 
	USES_ERROR_HANDLING;

	DM_TRY
    {
        ModelServices ^	model = gcnew ModelServices(in_pModel, this);
		AlgorithmBase  ^ algo = CreateAlgorithm(model);
		

		if( algo == nullptr )
		{
			*out_ppAlgo  = NULL;
			CURRENT_STATUS = S_OK;
		}
		else
		{
			algo->SetModelServices(model);
			algo->CustomFunctions = m_rgCustomFunctions;
			System::IntPtr wrappedUnk = System::Runtime::InteropServices::Marshal::GetIUnknownForObject( algo );
			IUnknown*	pUnk = (IUnknown*)(INT_PTR)wrappedUnk;
			
			CComPtr<IUnknown> spUnk(pUnk);
			CURRENT_STATUS = spUnk->QueryInterface( __uuidof(IDMAlgorithm), (void**)out_ppAlgo );
			
		}
		
    }
    DM_CATCHALL;
    
	REPORT_STATUS;
}



// Interface ICLR_DMALgorithmMetadata
HRESULT AlgorithmMetadataBase::ExportedGetServiceType(
                                IDMContextServices* in_pContext ,
                                ULONG*              out_pServiceTypeID)
{ 
    AutoPushContext autoCtx(in_pContext);
	PlugInServiceType   type  =   PlugInServiceType::ServiceTypeOther;
    USES_ERROR_HANDLING;

    DM_TRY
    {
        type    =   GetServiceType();

		*out_pServiceTypeID =   0;
		switch( type )
		{
			case PlugInServiceType::ServiceTypeOther:
				*out_pServiceTypeID =   0;
			case PlugInServiceType::ServiceTypeClassification:
				*out_pServiceTypeID =   DM_SERVICETYPE_CLASSIFICATION;
			case PlugInServiceType::ServiceTypeClustering:
				*out_pServiceTypeID =   DM_SERVICETYPE_CLUSTERING;
			case PlugInServiceType::ServiceTypeAssociation:
				*out_pServiceTypeID =   DM_SERVICETYPE_ASSOCIATION;
			case PlugInServiceType::ServiceTypeDensityEstimate:
				*out_pServiceTypeID =   DM_SERVICETYPE_DENSITY_ESTIMATE;
			case PlugInServiceType::ServiceTypeSequence:
				*out_pServiceTypeID =   DM_SERVICETYPE_SEQUENCE;
		}
    }
    DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetServiceName(
                                IDMContextServices* in_pContext ,
                                DMString*           io_pstrServiceName)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;


    String ^    name    =   nullptr;
    DM_TRY
    {
        name    =   GetServiceName();
    
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );

		CComPtr<IDMStringHandler> spStrHandler;
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrServiceName, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
    }
    DM_CATCHALL;


    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetDisplayName(
                                IDMContextServices* in_pContext ,
                                DMString*           io_pstrDescription)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;


	DM_TRY
	{
		String ^    name    =   nullptr;
        name    =   GetDisplayName();
	    
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );

		CComPtr<IDMStringHandler> spStrHandler;
		
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );
		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrDescription, bstrTmp, (UINT)name->Length) );

		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;


    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetServiceGuid(
                                IDMContextServices* in_pContext ,
                                GUID*				out_pguidServiceGUID)
{   
    AutoPushContext autoCtx(in_pContext);
    System::Guid^   guid;
    USES_ERROR_HANDLING;

	DM_TRY
	{
		
		guid    =   gcnew System::Guid(GetServiceGuid());

		array<unsigned char>^     guidByteArray =   guid->ToByteArray();
		pin_ptr<unsigned char>	pinnedGuid  =   &guidByteArray[0];
	    memcpy( out_pguidServiceGUID, pinnedGuid, sizeof(GUID) );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetDescription(
                                IDMContextServices* in_pContext ,
                                DMString*           io_pstrDescription)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

    DM_TRY
	{
		String ^    name    =   nullptr;
        name    =   GetServiceDescription();

		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );
		
		CComPtr<IDMStringHandler> spStrHandler;
		
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );
		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrDescription, bstrTmp, (UINT)name->Length) );

		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp);
	}
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetPredictionLimit(
                                IDMContextServices* in_pContext ,
                                LONG*               out_pPredictionLimit)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{
		// The maximum number of predictions the model and algorithm 
		// can provide; 0 means no limit. In most cases, this should
		// be no limit.
		long lPredictionLimit = 0;
		*out_pPredictionLimit = 0;
	    
		lPredictionLimit        =   GetPredictionLimit();
		*out_pPredictionLimit   =   lPredictionLimit;
	}
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetSupDistributionFlags(
                                IDMContextServices* in_pContext ,
                                ULONG**             out_prgFlags,
                                ULONG*              out_pcFlags)
{ 
   	AutoPushContext autoCtx(in_pContext);
    // In the current server, the appropriate flags are not defined in dmalgo.h

    *out_prgFlags   =   NULL;
    *out_pcFlags    =   0;
    return S_OK;
}


HRESULT AlgorithmMetadataBase::ExportedGetSupInputContentTypes(
                                IDMContextServices* in_pContext ,
                                ULONG**             out_prgTypes,
                                ULONG*              out_pcTypes)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{

		if( m_rgSupInputContentTypes == NULL )
		{
			array<MiningColumnContent>^  rgTypes = nullptr;
			rgTypes =   GetSupInputContentTypes();
			m_cSupInputContentTypes   =   (rgTypes == nullptr) ? 0 : ((ULONG)rgTypes->Length);
            
			// Use the CRT heap as the context allocator allocations cannot 
			// be cached between calls
			m_rgSupInputContentTypes  =   new ULONG[m_cSupInputContentTypes];
			for( ULONG nIndex = 0; nIndex < m_cSupInputContentTypes; nIndex ++ )
			{
				MiningColumnContent type =   rgTypes[nIndex];
				m_rgSupInputContentTypes[nIndex] = (ULONG)type;
			}
		}

		*out_pcTypes    =   m_cSupInputContentTypes;
		*out_prgTypes   =   m_rgSupInputContentTypes;
	}
	DM_CATCHALL;

    REPORT_STATUS;
}



HRESULT AlgorithmMetadataBase::ExportedGetSupPredictContentTypes(
                                IDMContextServices* in_pContext ,
                                ULONG**             out_prgTypes,
                                ULONG*              out_pcTypes)
{ 
    AutoPushContext autoCtx(in_pContext);

	USES_ERROR_HANDLING;

	DM_TRY
	{
		if( m_rgSupPredictContentTypes == NULL )
		{
			array<MiningColumnContent>^  rgTypes = nullptr;
			rgTypes =   GetSupPredictContentTypes();
			m_cSupPredictContentTypes   =   (rgTypes == nullptr) ? 0 : ((ULONG)rgTypes->Length);
            
			// Use the CRT heap as the context allocator allocations cannot 
			// be cached between calls
			m_rgSupPredictContentTypes  =   new ULONG[m_cSupPredictContentTypes];
			for( ULONG nIndex = 0; nIndex < m_cSupPredictContentTypes; nIndex ++ )
			{
				MiningColumnContent type =   rgTypes[nIndex];
				m_rgSupPredictContentTypes[nIndex] = (ULONG)type;
			}
		}

		*out_pcTypes    =   m_cSupPredictContentTypes;
		*out_prgTypes   =   m_rgSupPredictContentTypes;
	}
	DM_CATCHALL;

    REPORT_STATUS;
}



HRESULT AlgorithmMetadataBase::ExportedGetSupModelingFlags(
                                IDMContextServices* in_pContext ,
                                DM_MODELING_FLAG**  out_prgFlags,
                                ULONG*              out_pcFlags)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{
		if( m_rgSupModelingFlags == NULL )
		{
			array<MiningModelingFlag>^  rgFlags = nullptr;
			rgFlags =   GetSupModelingFlags();
			m_cSupModelingFlags   =   (rgFlags != nullptr) ? ((ULONG)rgFlags->Length): 0;
            
			// Use the CRT heap as the context allocator allocations cannot 
			// be cached between calls
			m_rgSupModelingFlags  =   new DM_MODELING_FLAG[m_cSupModelingFlags];
			for( ULONG nIndex = 0; nIndex < m_cSupModelingFlags; nIndex ++ )
			{
				MiningModelingFlag flag =   rgFlags[nIndex];
				m_rgSupModelingFlags[nIndex] = (DM_MODELING_FLAG)flag;
			}
		}

		*out_pcFlags    =   m_cSupModelingFlags;
		*out_prgFlags   =   m_rgSupModelingFlags;
	}
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetModelingFlagName(
                                IDMContextServices* in_pContext ,
                                DM_MODELING_FLAG	in_Flag,
                                DMString*           io_pstrFlagName)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{
		String ^    name    =   nullptr;
		MiningModelingFlag  flag = (MiningModelingFlag)in_Flag;
		name    =   GetModelingFlagName(flag);

		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );

		CComPtr<IDMStringHandler> spStrHandler;

		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );
		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrFlagName, bstrTmp, (UINT)name->Length) );

		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}



HRESULT AlgorithmMetadataBase::ExportedGetTrainingComplexity(	
                                            IDMContextServices*	in_pContext,
                                            LONG*					out_pTrainingComplexity)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;
	DM_TRY
	{
		*out_pTrainingComplexity    =   (LONG)GetTrainingComplexity();
	}
	DM_CATCHALL;

	REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetPredictionComplexity			(	
                                            IDMContextServices*	in_pContext,
                                            LONG*					out_pPredictionComplexity	)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;
	
	DM_TRY
	{
        *out_pPredictionComplexity    =   (LONG)GetPredictionComplexity();
	}
	DM_CATCHALL;
    
	REPORT_STATUS;

}


HRESULT AlgorithmMetadataBase::ExportedGetExpectedQuality				(	
                                            IDMContextServices*	in_pContext,
                                            LONG*					out_pExpectedQuality		)
{ 
    AutoPushContext autoCtx(in_pContext);

	USES_ERROR_HANDLING;
	DM_TRY
	{
        *out_pExpectedQuality    =   (LONG)GetExpectedQuality();
    }
	DM_CATCHALL;

	REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetScaling						(	
                                            IDMContextServices*	in_pContext,
                                            LONG*					out_pScaling				)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{
        *out_pScaling    =   (LONG)GetScaling();
    }
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetAllowIncrementalInsert		(	
                                            IDMContextServices*	in_pContext,
                                            BOOL*					out_pfIncrementalInsert		)
{ 
    AutoPushContext autoCtx(in_pContext);

	USES_ERROR_HANDLING;
	DM_TRY
	{
        *out_pfIncrementalInsert    =   GetAllowIncrementalInsert() ? TRUE : FALSE;
    }
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetAllowDuplicateKey			(	
                                            IDMContextServices*	in_pContext,
                                            BOOL*					out_pfDuplicateKey			)
{ 
    AutoPushContext autoCtx(in_pContext);

	USES_ERROR_HANDLING;
	DM_TRY
	{
		*out_pfDuplicateKey    =   GetAllowDuplicateKey() ? TRUE : FALSE;
    }
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetControl						(	
                                            IDMContextServices*	in_pContext,
                                            LONG*					out_pControl				)
{ 
    AutoPushContext autoCtx(in_pContext);

	USES_ERROR_HANDLING;
	DM_TRY
	{
        *out_pControl    =   (LONG)GetControl();
    }
    DM_CATCHALL;
    
	REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetViewerType					(	
                                            IDMContextServices*	in_pContext,
                                            DMString *			io_pstrViewerType			)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{
	    String ^    name    =   nullptr;
        name    =   GetViewerType();
	
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );
	
		CComPtr<IDMStringHandler> spStrHandler;
	
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );
	
		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrViewerType, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;

}


HRESULT AlgorithmMetadataBase::ExportedGetSupportsDMDimensions			(	
                                            IDMContextServices*	in_pContext,
                                            BOOL*					out_pfSupportsDMDimensions	)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{
        *out_pfSupportsDMDimensions    =   (LONG)GetSupportsDMDimensions();
    }
	DM_CATCHALL;

    REPORT_STATUS;

}


HRESULT AlgorithmMetadataBase::ExportedGetSupportsDrillthrough			(	
                                            IDMContextServices*	in_pContext,
                                            BOOL*					out_pfSupportsDrillthrough,
                                            BOOL*					out_pfMustIncludeChildren	)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;
    
	DM_TRY
    {
        *out_pfSupportsDrillthrough     =   GetSupportsDrillThrough() ? TRUE : FALSE;
        *out_pfMustIncludeChildren      =   GetDrillThroughMustIncludeChildren() ? TRUE : FALSE;
    }
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetSupportedFunctions			(	
                                            IDMContextServices*	in_pContext,
                                            DM_SUPPORTED_FUNCTION **	out_prgFuncs,
                                            ULONG *				out_cFuncs					)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;


	DM_TRY
	{
		if( m_rgSupFunctions == NULL )
		{
			array<SupportedFunction>^  rgFunc = nullptr;
			rgFunc =   GetSupportedStandardFunctions();
			ULONG		cStdFunctions = (rgFunc == nullptr) ? 0 : rgFunc->Length;

			// Add the standard functions and the custom functions
			m_cSupFunctions   =   cStdFunctions + m_rgCustomFunctions->Length;
	        
			// Use the CRT heap as the context allocator allocations cannot 
			// be cached between calls
			m_rgSupFunctions  =   new DM_SUPPORTED_FUNCTION[m_cSupFunctions];
			for( ULONG nIndex = 0; nIndex < cStdFunctions; nIndex ++ )
			{
				SupportedFunction func =   rgFunc[nIndex];
				m_rgSupFunctions[nIndex] = (DM_SUPPORTED_FUNCTION)func;
			}

			for( int nIndex = 0; nIndex < m_rgCustomFunctions->Length; nIndex ++ )
			{
				m_rgSupFunctions[nIndex + cStdFunctions] = DMSF_CUSTOM_FUNCTION_BASE + nIndex;
			}
		}

		*out_cFuncs     =   m_cSupFunctions;
		*out_prgFuncs   =   m_rgSupFunctions;
	}
	DM_CATCHALL;

    REPORT_STATUS;

}

//-------------------------------------------------------------------------
// Service parameters access


HRESULT AlgorithmMetadataBase::ExportedGetNumParameters				(	
                                            IDMContextServices*	in_pContext,
                                            ULONG*				out_pcParameters			)
{ 
    AutoPushContext autoCtx(in_pContext);

	USES_ERROR_HANDLING;

    DM_TRY
    {
        EnsureParameters();
        if( m_params == nullptr )
        {
            *out_pcParameters = 0;
        }
        else
        {
            *out_pcParameters = (ULONG)m_params->Count;
        }
    }
    DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetParameterName				(	
                                            IDMContextServices*	in_pContext,
                                            ULONG					in_iParameter,
                                            DMString *			io_pstrParameterName		)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{
		String ^    name    =   nullptr;
        EnsureParameters();
        name    =   m_params[in_iParameter]->Name;

		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );
		
		CComPtr<IDMStringHandler> spStrHandler;
		
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );
		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrParameterName, bstrTmp, (UINT)name->Length) );

		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetParameterType				(	
                                            IDMContextServices*	in_pContext,
                                            ULONG					in_iParameter,
                                            DMString *			io_pstrParameterType		)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{
	    String ^    name    =   nullptr;
        EnsureParameters();

		name    =   m_params[in_iParameter]->DbTypeName;
    
    
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );
		
		CComPtr<IDMStringHandler> spStrHandler;
		
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrParameterType, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*) bstrTmp);
	}
	DM_CATCHALL;

    REPORT_STATUS;

}


HRESULT AlgorithmMetadataBase::ExportedGetParameterIsRequired			(	
                                            IDMContextServices*	in_pContext,
                                            ULONG					in_iParameter,
                                            BOOL*					out_pfIsRequired			)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;
    
	DM_TRY
    {
        EnsureParameters();
        *out_pfIsRequired   =   m_params[in_iParameter]->IsRequired ? TRUE : FALSE;
    }
	DM_CATCHALL;
    
	REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetParameterIsExposed			(	
                                            IDMContextServices*	in_pContext,
                                            ULONG					in_iParameter,
                                            BOOL*					out_pfIsExposed				)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;


    DM_TRY
    {
        EnsureParameters();
        *out_pfIsExposed   =   m_params[in_iParameter ]->IsExposed ? TRUE : FALSE;
    }
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetParameterFlags				(	
                                            IDMContextServices*	in_pContext,
                                            ULONG					in_iParameter,
                                            ULONG*				out_pulParameterFlags		)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;
	
	DM_TRY
    {
        EnsureParameters();
        *out_pulParameterFlags   =   (ULONG)m_params[in_iParameter]->Flags;
    }
	DM_CATCHALL;
    
	REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetParameterDescription			(	
                                            IDMContextServices*	in_pContext,
                                            ULONG				in_iParameter,
                                            ULONG				in_LocaleID,
                                            DMString *			io_pstrDescription			)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;


    // TODO: Use the actual locale ID

	DM_TRY
	{
		String ^    name    =   nullptr;
        EnsureParameters();
        name    =   m_params[in_iParameter]->Description;
    
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );

		CComPtr<IDMStringHandler> spStrHandler;
	
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrDescription, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetParameterDefaultValue		(	
                                            IDMContextServices*	in_pContext,
                                            ULONG					in_iParameter,
                                            DMString *			io_pstrDefaultValue			)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

    // TODO: Use the actual locale ID
    DM_TRY
	{
		String ^    name    =   nullptr;
        EnsureParameters();
        name    =   m_params[in_iParameter]->DefaultValue;
    
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );

		CComPtr<IDMStringHandler> spStrHandler;
		
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrDefaultValue, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetParameterValueEnumeration	(	
                                            IDMContextServices*	in_pContext,
                                            ULONG					in_iParameter,
                                            DMString *			io_pstrValueEnumeration		)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

    DM_TRY
	{
		String ^    name    =   nullptr;
        EnsureParameters();
        name    =   m_params[in_iParameter]->ValueEnumeration;
	
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );

		CComPtr<IDMStringHandler> spStrHandler;
		
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrValueEnumeration, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;

}


HRESULT AlgorithmMetadataBase::ExportedParseParameterValue				(	
                                            IDMContextServices*	in_pContext,
                                            ULONG					in_iParameter,
                                            DMString *			    in_pstrParameterStrValue,
                                            DMVariantPtr			io_pParameterMappedValue	)
{ 
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	VARIANT var;
	::VariantInit(&var);

	DM_TRY
	{
		CComPtr<IDMStringHandler> spStrHandler;
		CComPtr<IDMVariantPtrHandler> spVarHandler;
		
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );
		THROW_ON_FAILURE( in_pContext->GetVariantHandler( &spVarHandler) );

	    
		const wchar_t *wchParamValue  = NULL;
		UINT    uLength         = 0;
		THROW_ON_FAILURE( spStrHandler->GetConstStringFromHandle( in_pstrParameterStrValue, &wchParamValue, &uLength) );

		EnsureParameters();

		String ^    paramValue = gcnew String(wchParamValue, 0, uLength);
		Object ^    objValue    =   ParseParameterValue(in_iParameter, paramValue);

		MiningParameter::ConvertRuntimeToVariant(in_pContext, objValue, &var);

		THROW_ON_FAILURE( spVarHandler->CopyVariantToHandle(io_pParameterMappedValue, &var) );
	}
	DM_CATCHALL
	DM_FINALLY
	{
		::VariantClear( &var );
	}


    REPORT_STATUS;
}

//-------------------------------------------------------------------------
// Algorithm Limitations and requirements


HRESULT AlgorithmMetadataBase::ExportedGetMarginalRequirements			(	
                                            IDMContextServices*	in_pContext,
                                            DMMarginalRequirements*	out_pReq)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{
        *out_pReq = (DMMarginalRequirements)GetMarginalRequirements();
    }
	DM_CATCHALL;
    
	REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetCaseIDModelled				(	
                                            IDMContextServices*	in_pContext,					
                                            BOOL*					out_pfIsModelled)
{ 
    AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

    DM_TRY
    {
        *out_pfIsModelled = GetCaseIdModeled() ?  TRUE : FALSE;
    }
	DM_CATCHALL;

    REPORT_STATUS;
}

//-------------------------------------------------------------------------
// Validation: Each algorithm has to validate the model using IDMAttributeSet.
// Note that IDMAttributeSet has not been fully populated in that we have not
// actually read the training cases yet. For instance, # of states, # of
// attributes in a nested table won't be available at this time. However,
// it's been populated enough to validate the structure of the model.
//  The only valid methods to call are:
//		GetAttributeFlags
//		GetAttributeCount		(returns the number of attributes you can query on, not the actual attributes that will be in the dataset)
//		IsFunctionallyDependant
//		IsCaseAttribute
//
//-------------------------------------------------------------------------

HRESULT AlgorithmMetadataBase::ExportedValidateAttributeSet			(	
                                            IDMContextServices*	in_pContext,
                                            IDMAttributeSet*		in_pAttributeSet)
{ 
    AutoPushContext autoCtx(in_pContext);

	USES_ERROR_HANDLING;
    
	DM_TRY
	{
		AttributeSet ^  attrSet =   gcnew AttributeSet( in_pAttributeSet );
		attrSet->LimitForValidation();

        ValidateAttributeSet( attrSet );
    }
	DM_CATCHALL;

    REPORT_STATUS;
}




//-------------------------------------------------------------------------
// Metadata should return TRUE only if the algorithm supports one of the following operations:
//	- initialization from PMML
//	- generating PMML 
//-------------------------------------------------------------------------


HRESULT AlgorithmMetadataBase::ExportedGetSupportsPMML			(	
                                            IDMContextServices*	in_pContext,
                                            BOOL*					out_pfSupportsPMML)
{ 
    AutoPushContext autoCtx(in_pContext);
    
	USES_ERROR_HANDLING;

    DM_TRY
    {
		bool fSupportsPMML	=	this->SupportsPMML();
		*out_pfSupportsPMML	=	fSupportsPMML ? TRUE : FALSE;
    }
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT AlgorithmMetadataBase::ExportedGetName(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
    DMString *			io_pstrName)
{

    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{

		String ^    name    =   nullptr;

		CustomFunctionInfo^	funcInfo = ValidateFunction(in_Function, m_rgCustomFunctions);
		name    =   funcInfo->Name;

		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );
		
		CComPtr<IDMStringHandler> spStrHandler;
		
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrName, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}



HRESULT AlgorithmMetadataBase::ExportedGetSignature(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
    DMString *			io_pstrSignature)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;


    DM_TRY
	{
		String ^    name    =   nullptr;
		CustomFunctionInfo^	funcInfo = ValidateFunction(in_Function, m_rgCustomFunctions);
        name    =   funcInfo->Signature;
    
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );


		CComPtr<IDMStringHandler> spStrHandler;
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrSignature, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}




HRESULT AlgorithmMetadataBase::ExportedGetReturnsTable(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
    BOOL*			out_pfReturnsTable)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;


	DM_TRY
	{
		String ^    name    =   nullptr;
		CustomFunctionInfo^	funcInfo	=	ValidateFunction(in_Function, m_rgCustomFunctions);
		*out_pfReturnsTable =   funcInfo->ReturnsTable ? TRUE : FALSE;
    }
	DM_CATCHALL;


	REPORT_STATUS;
}



HRESULT AlgorithmMetadataBase::ExportedGetDescription(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
    DMString *			io_pstrDescription)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;


	DM_TRY
	{
		String ^    name    =   nullptr;
		CustomFunctionInfo^	funcInfo = ValidateFunction(in_Function, m_rgCustomFunctions);
        name    =   funcInfo->Description;
	
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );

		
		CComPtr<IDMStringHandler> spStrHandler;
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrDescription, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}




HRESULT AlgorithmMetadataBase::ExportedGetHelpFile(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
    DMString *			io_pstrHelpFile)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{
		String ^    name    =   nullptr;
		CustomFunctionInfo^	funcInfo = ValidateFunction(in_Function, m_rgCustomFunctions);
        name    =   funcInfo->HelpFile;
	
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );
		
		CComPtr<IDMStringHandler> spStrHandler;
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );

		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrHelpFile, bstrTmp, (UINT)name->Length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}




HRESULT AlgorithmMetadataBase::ExportedGetHelpContext(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
    LONG *			io_pHelpContext)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;


	DM_TRY
	{
		CustomFunctionInfo^	funcInfo =	ValidateFunction(in_Function, m_rgCustomFunctions);
		*io_pHelpContext =   funcInfo->HelpContext;
    }
	DM_CATCHALL;

	REPORT_STATUS;
}



HRESULT AlgorithmMetadataBase::ExportedGetNumParameters(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
    ULONG *			out_pcParameters)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{
		CustomFunctionInfo^	funcInfo =	ValidateFunction(in_Function, m_rgCustomFunctions);
		*out_pcParameters =   funcInfo->ParametersCount;
    }
	DM_CATCHALL;

	REPORT_STATUS;
}



HRESULT AlgorithmMetadataBase::ExportedGetNumFlags(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
    ULONG *			out_pcFlags)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

    DM_TRY
	{
		CustomFunctionInfo^	funcInfo =	ValidateFunction(in_Function, m_rgCustomFunctions);
		*out_pcFlags =   funcInfo->FlagsCount;
    }
	DM_CATCHALL;

	REPORT_STATUS;
}




HRESULT AlgorithmMetadataBase::ExportedGetParameterType(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
	ULONG				in_iParameter,
    DM_FUNC_PARAM_TYPE*	out_pType)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

	DM_TRY
	{
		CustomFunctionInfo^	funcInfo =	ValidateFunction(in_Function, m_rgCustomFunctions);
		CustomFunctionParameterInfo^ paramInfo = funcInfo->Parameter[in_iParameter];
		*out_pType=   paramInfo->ParamType;
    }
	DM_CATCHALL;
	REPORT_STATUS;
}




HRESULT AlgorithmMetadataBase::ExportedGetParameterDataType(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
	ULONG				in_iParameter,
    VARTYPE*	out_pDataType)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

    DM_TRY
    {
		CustomFunctionInfo^	funcInfo =	ValidateFunction(in_Function, m_rgCustomFunctions);
		CustomFunctionParameterInfo^ paramInfo = funcInfo->Parameter[in_iParameter];
		*out_pDataType=   paramInfo->DataType;
    }
	DM_CATCHALL;

	REPORT_STATUS;
}




HRESULT AlgorithmMetadataBase::ExportedGetParameterIsRequired(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
	ULONG				in_iParameter,
    BOOL*	out_pfIsRequired)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;

    DM_TRY
    {
		CustomFunctionInfo^	funcInfo =	ValidateFunction(in_Function, m_rgCustomFunctions);
		CustomFunctionParameterInfo^ paramInfo = funcInfo->Parameter[in_iParameter];
		*out_pfIsRequired=   paramInfo->IsRequired;
    }
	DM_CATCHALL;

	REPORT_STATUS;
}




HRESULT AlgorithmMetadataBase::ExportedGetFlagName(
	IDMContextServices* in_pContext,
	DM_SUPPORTED_FUNCTION in_Function,
	ULONG			in_iFlag,
    DMString *		io_pstrName)
{
    AutoPushContext autoCtx(in_pContext);
    USES_ERROR_HANDLING;


	DM_TRY
	{
		String ^    name    =   nullptr;

		CustomFunctionInfo^	funcInfo = ValidateFunction(in_Function, m_rgCustomFunctions);
        CustomFunctionFlagInfo^ flagInfo = funcInfo->Flag[in_iFlag];
		name = flagInfo->Name;
	
		BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( name );
	
		CComPtr<IDMStringHandler> spStrHandler;
	
		THROW_ON_FAILURE( in_pContext->GetStringHandler( &spStrHandler) );
		THROW_ON_FAILURE( spStrHandler->CopyBufferToHandle( io_pstrName, bstrTmp, (UINT)name->Length) );

		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
	}
	DM_CATCHALL;

    REPORT_STATUS;
}


HRESULT	AlgorithmMetadataBase::ExportedInterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&__uuidof(IDMAlgorithmFactory),
		&__uuidof(IDMAlgorithmMetadata),
		&__uuidof(IDMCustomFunctionInfo),
	};

	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}



CustomFunctionInfo^		AlgorithmMetadataBase::ValidateFunction(DM_SUPPORTED_FUNCTION	in_FunctionOrdinal, array<CustomFunctionInfo^>^ in_rgSuppFunctions)
{
	int nIndex = 0;
	nIndex = in_FunctionOrdinal - DMSF_CUSTOM_FUNCTION_BASE;
	if( nIndex < 0 || nIndex >= in_rgSuppFunctions->Length )
	{
		throw gcnew System::ArgumentOutOfRangeException(
									gcnew System::String(L"in_FunctionOrdinal"),
									gcnew Int32(in_FunctionOrdinal),
									System::String::Empty
								);
	}

	return in_rgSuppFunctions[nIndex];
}


System::String^	AlgorithmMetadataBase::GetServiceGuid()
{
	System::Type^ thisType = this->GetType();
	array<Object^>^ rgGuidAttrs = thisType->GetCustomAttributes(System::Runtime::InteropServices::GuidAttribute::typeid, false);
	if( rgGuidAttrs->Length == 0 )
		return String::Empty;

	System::Runtime::InteropServices::GuidAttribute^ guidAttr = dynamic_cast<System::Runtime::InteropServices::GuidAttribute^>(rgGuidAttrs[0]);

	return guidAttr->Value;
}

void AlgorithmMetadataBase::BuildCustomFunctionInfo()
{
	
	System::Type^ thisType = this->GetType();
	System::Type^ AlgoType = nullptr;
	
	// Find the Algorithm type
	MiningAlgorithmClassAttribute^ algoTypeAttr = nullptr;
	array<Object^>^ rgMiningAlgoClassAttrs = thisType->GetCustomAttributes(MiningAlgorithmClassAttribute::typeid, false);
	if( rgMiningAlgoClassAttrs->Length == 0 )
	{
		return;
	}
	algoTypeAttr = dynamic_cast<MiningAlgorithmClassAttribute^>(rgMiningAlgoClassAttrs[0]);
	AlgoType     = algoTypeAttr->AlgorithmClass;

	// Now traverse the methods of the Algorithm class and build CustomFunctionInfo objects
	array<MethodInfo^>^ arMI = AlgoType->GetMethods();

	m_rgCustomFunctions = gcnew array<CustomFunctionInfo^>(0);
	
	
	for( int nIndex = 0; nIndex < arMI->Length; nIndex ++)
	{
		MethodInfo^	mi = arMI [nIndex];
		
		array< Attribute^>^ custAttrs = Attribute::GetCustomAttributes(mi);

		for( int nAttIndex = 0; nAttIndex < custAttrs->Length; nAttIndex ++ )
		{
			Attribute^	attr = custAttrs[nAttIndex];

			if( attr->GetType() == MiningFunctionAttribute::typeid )
			{
				CustomFunctionInfo^	funcInfo = PopulateCustomFunctionInfo( mi, dynamic_cast<MiningFunctionAttribute^>(attr) );
				int nIndex = m_rgCustomFunctions->Length;
				Array::Resize(m_rgCustomFunctions, nIndex + 1);
				m_rgCustomFunctions[nIndex] = funcInfo;
			}
		}
	}
}

CustomFunctionInfo^ AlgorithmMetadataBase::PopulateCustomFunctionInfo(System::Reflection::MethodInfo ^mi, PluginAlgorithms::MiningFunctionAttribute ^attrInfo)
{
	CustomFunctionInfo^	funcInfo = gcnew CustomFunctionInfo(mi);

	funcInfo->SetValid();
	
	try
	{
		funcInfo->HelpFile = attrInfo->HelpFile ;
		funcInfo->HelpContext = attrInfo->HelpContext;
		funcInfo->Description = attrInfo->Description;

		

		funcInfo->ReturnType = mi->ReturnType;

		if( mi->ReturnType->IsValueType || 
			mi->ReturnType == Object::typeid || 
			mi->ReturnType == System::Data::DataTable::typeid ||
			mi->ReturnType == System::String::typeid)
		{
			funcInfo->ReturnType = mi->ReturnType;
		}
		else 
		{
			funcInfo->ReturnType = mi->ReturnType;
			throw gcnew System::Exception("Invalid return type in a custom method (" + mi->Name + "):" + mi->ReturnType->Name);
		}

		// Traverse the parameters to render the signature
		String^ strSig = gcnew String(L"");
		strSig	+=	mi->Name;
		strSig	+=	L"(";
		

		array<ParameterInfo^>^	rgParams = mi->GetParameters();
		int	nCols = 0;


		
		for( int nIndex = 0; nIndex < rgParams->Length; nIndex ++ )
		{
			String^ strCol	=	nullptr;
			ParameterInfo^	pi	=	rgParams[nIndex];

			// Simple type -- may be ScalarColRef or Constant
			bool fIsFlag = false;
			array<Object^>^		rgFlagAttr = pi->GetCustomAttributes( FlagAttribute::typeid, false);
			if( rgFlagAttr->Length > 0 )
				fIsFlag = true;

			Type^	uType = pi->ParameterType->UnderlyingSystemType;
			if( uType->IsValueType || uType == Object::typeid)
			{
				if( fIsFlag )
				{
					CustomFunctionFlagInfo^	currFlag = gcnew CustomFunctionFlagInfo();
					if( uType == Boolean::typeid)
					{
						strCol =String::Format(CultureInfo::InvariantCulture,  L"[, {0}]", pi->Name);
						currFlag->Name = pi->Name;
						currFlag->ParamIndex = nIndex;
					}
					else
					{
						throw gcnew System::Exception("Invalid Flag type (must be Boolean) in method " + mi->Name + ":" + pi->Name);
					}
					funcInfo->AddFlag( currFlag );
				}
				else
				{
					CustomFunctionParameterInfo^	currParam = gcnew CustomFunctionParameterInfo();
					currParam->ParamIndex = nIndex;
					// currently, all the parameters are required
					currParam->IsRequired = true;
					currParam->ManagedType = uType;

					// Make sure it is not marked as a ref
					array<Object^>^		rgTableRef = pi->GetCustomAttributes( MiningTableColumnRefAttribute::typeid, false);
					array<Object^>^		rgScalarRef = pi->GetCustomAttributes( MiningScalarColumnRefAttribute::typeid, false);
					if( rgTableRef->Length > 0 || rgScalarRef->Length > 0 )
					{
						throw gcnew System::Exception("Invalid ...ColumnRef Attribute usage in method " + mi->Name + ", parameter:" + pi->Name);
					}

					// Enumerations are NOT supported
					if( uType->IsEnum )
					{
						throw gcnew System::Exception("Invalid parameter type in method " + mi->Name + ", parameter:" + pi->Name + ". Enumerations are not supported.");
					}


					currParam->ParamType = DMFPT_CONST_EXPRESSION;
					currParam->DataType  = VT_NULL;

					
					String^ paramType = nullptr;
					if( uType!= Object::typeid)
					{
						paramType = uType->Name;
					}
					// Constant
					if( paramType != nullptr )
					{
						strCol = String::Format(CultureInfo::InvariantCulture,   L"<Typed({0}) Constant>", paramType);
						currParam->DataType  = VT_NULL;

						currParam->DataType =	VARTYPEFromSystemType(uType);
						if( currParam->DataType == VT_VARIANT )
						{
							throw gcnew System::Exception("Invalid parameter type in method " + mi->Name + ", parameter:" + pi->Name + ". Type " + paramType + " is not supported.");
						}
					}
					else
					{
						strCol =gcnew String(L"<Untyped Constant>");
						currParam->DataType  = VT_VARIANT;
					}

					funcInfo->AddParameter( currParam );
				}
			}
			else
			{
				if (uType== AttributeGroup::typeid )
				{
					CustomFunctionParameterInfo^	currParam = gcnew CustomFunctionParameterInfo();
					currParam->ParamIndex = nIndex;
					// currently, all the parameters are required
					currParam->IsRequired = true;
					currParam->ManagedType = uType;		

					
					// Simple type -- may be ScalarColRef or Constant
					array<Object^>^		rgAttributes = pi->GetCustomAttributes( MiningTableColumnRefAttribute::typeid, false);
					if( rgAttributes->Length > 0 )
					{
						strCol =	gcnew String(L"<Table column reference>");
						currParam->ParamType = DMFPT_TABLE_COLUMN_REF;
						currParam->DataType  = VT_NULL;
					}
					else
					{
						strCol =	gcnew String(L"<Scalar column reference>");
						currParam->ParamType = DMFPT_SCALAR_COLUMN_REF;
						currParam->DataType  = VT_NULL;
					}

					funcInfo->AddParameter( currParam );

				}
				else if( uType== MiningCase::typeid) 
				{
					// The current case wrapper is not part of the signature
					if( funcInfo->CurrentCase != nullptr )
					{
						throw gcnew System::Exception("Only one MiningCase parameter is supported. Method: " + mi->Name + ", 2nd MiningCase parameter:" + pi->Name);
					}
					CustomFunctionParameterInfo^	currParam = gcnew CustomFunctionParameterInfo();
					currParam->ParamIndex = nIndex;
					
					// currently, all the parameters are required
					currParam->IsRequired = false;
					currParam->ManagedType = uType;		

					currParam->ParamType = DMFPT_UNKNOWN;
					currParam->DataType  = VT_NULL;

					funcInfo->CurrentCase  = currParam;
					continue;
				}
				else 
				{
					throw gcnew System::Exception("Invalid parameter type in method " + mi->Name + ", parameter:" + pi->Name + ". Type " + pi->ParameterType->Name + " is not supported.");
				}
			}
			
			if( strCol != nullptr )
			{
				if( nCols > 0 )
				{
					if(!fIsFlag)
					{
						strSig	+=	L",";
					}
					strSig += L" ";
				}
				
				strSig += strCol;
				nCols++;
			}
		}
		strSig	+=	L")";
		funcInfo->Signature = strSig;
	}
	catch( System::Exception^ ex)
	{
		if( funcInfo->Valid )
		{
			funcInfo->SetInvalid(ex->Message);
		}
		// Override the description
		funcInfo->Description = "ERROR:" + funcInfo->Error;
	}


	return funcInfo;
}

VARTYPE	AlgorithmMetadataBase::VARTYPEFromSystemType(Type^ type)
{
	VARTYPE vRet	=	VT_NULL;
	switch( Type::GetTypeCode( type ) )
	{
	case TypeCode::Boolean:
		vRet = VT_BOOL;
		break;
	case TypeCode::Byte:
		vRet = VT_UI1;
		break; 
	case TypeCode::Char :
		vRet = VT_UI2;
		break;
	case TypeCode::DateTime :
		vRet = VT_DATE;
		break;
	case TypeCode::Double :
		vRet = VT_R8;
		break;
	case TypeCode::Int16 :
		vRet = VT_I2;
		break;
	case TypeCode::Int32 :
		vRet = VT_I4;
		break;
	case TypeCode::Int64 :
		vRet = VT_I8;
		break;
	case TypeCode::SByte :
		vRet = VT_I1;
		break;
	case TypeCode::Single:
		vRet = VT_R4;
		break;
	case TypeCode::String:
		vRet = VT_BSTR;
		break;
	case TypeCode::UInt16:
		vRet = VT_UI2;
		break;
	case TypeCode::UInt32:
		vRet = VT_UI4;
		break;
	case TypeCode::UInt64:
		vRet = VT_UI8;
		break;
	case TypeCode::DBNull :
	case TypeCode::Decimal:
	case TypeCode::Empty :
	case TypeCode::Object:
	default:
		vRet	=	VT_VARIANT;
		break;
	}
	return vRet;
}